/*
*  Box2D.cpp
*
*  Created on: Jan 17, 2012
*  
*/


#pragma region INCLUDEs
//
#include "Box2D.h"
#include "Circle2D.h"

#include <iostream>
using namespace std;

#include <cmath>
#include "MathHelper.h"

#pragma endregion



namespace Physics
{
	//Re-Declare static variable for Compiler Pre-Processing
	int Box2D::bx2D_InstanceCount = 0;
	
#pragma region CONSTRUCTORs

	//C001: DEFAULT Constructor
	Box2D::Box2D()
	{
		this->width = 1;
		this->height = 1;

		this->objID = ZERO; 
		++bx2D_InstanceCount;
	}	

	//C002: Overloaded Constructor
	Box2D::Box2D(const float width, const float height, const Vector2 &pos = Vector2())
	{
		this->width = ( width < 1 ) ? 1 : width;
		this->height = ( height < 1 ) ? 1 : height;

		this->position = pos;
		
		this->objID = ZERO; 
		++bx2D_InstanceCount;
	}

	//C003: Overloaded Constructor
	Box2D::Box2D(const float width, const float height, const Vector2 &pos = Vector2(), const Vector2 &dir = Vector2())
	{
		this->width = ( width < 1 ) ? 1 : width;
		this->height = ( height < 1 ) ? 1 : height;

		this->position = pos;
		this->direction = dir;
		
		this->objID = ZERO; 
		++bx2D_InstanceCount;
	}

	//C004: Overloaded Constructor
	Box2D::Box2D(const float width, const float height, const Vector2 &pos = Vector2(), const Vector2 &dir = Vector2(), const float speed = 0, const float mass = 0)
	{
		this->width = ( width < 1 ) ? 1 : width;
		this->height = ( height < 1 ) ? 1 : height;

		this->position = pos;
		this->direction = dir;

		this->speed = speed;
		this->mass = mass;
		
		this->objID = ZERO; 
		++bx2D_InstanceCount;
	}

	//C005: Copy Constructor
	Box2D::Box2D( const Box2D &box )
	{
		//
		this->width = box.getWidth();
		this->height = box.getHeight();

		this->position = box.getPosition();
		this->direction = box.getDirection();

		this->speed = box.getSpeed();
		this->mass = box.getMass();

		this->objID = box.getID();
		++bx2D_InstanceCount;
		//
	}

	//C006: Assignment Operator
	Box2D& Box2D::operator=( const Box2D &box )
	{
		//Check for NO self assignment
		if (this != &box) 
		{
			//Deallocate, allocate new space, copy values...
			this->width = box.getWidth();
			this->height = box.getHeight();

			this->position = box.getPosition();
			this->direction = box.getDirection();

			this->speed = box.getSpeed();
			this->mass = box.getMass();

			this->objID = box.getID(); 
			++bx2D_InstanceCount;
		}
		return *this;
	}

	//C008: Virtual Destructor
	Box2D::~Box2D() 
	{ 
		//decrement global active objects' counter
		this->decrement_ObjectCount(); 

		//decrement box2D Instance Counter
		( ( bx2D_InstanceCount > 0 ) ? --bx2D_InstanceCount : bx2D_InstanceCount = 0 );
	}
#pragma endregion

	
	//M014: Getter Instance Count
	const int Box2D::count()
	{
		return bx2D_InstanceCount;
	}


#pragma region WIDTH_PROPERTIES
	//M001: Setter Width
	void Box2D::setWidth(const float width)
	{
		this->width = ( width < 1 ) ? 1 : width;
	}

	//M002: Getter Width
	const float Box2D::getWidth() const  { return this->width; }
	//
#pragma endregion


#pragma region HEIGHT_PROPERTIES
	//M003: Setter Height
	void Box2D::setHeight(const float height)
	{
		this->height = ( height < 1 ) ? 1 : height;
	}

	//M004: Getter Height
	const float Box2D::getHeight() const  { return this->height; }
	//
#pragma endregion


#pragma region IPhysicsObject_2D_METHODs
	//M015: Print Summary Info about instance object
	void Box2D::Info()
	{
		cout << "\n======================================================\n" << endl;
		cout << "\nBox2D Obj Info:\n" << endl;
		cout << "\n--------------------------------------------------------\n" << endl;

		cout << "Object ID: " << this->objID << endl << endl;

		cout << "Position XY: (" << this->position.getX() << ", " << this->position.getY() << ")" << endl;
		cout << "Direction XY: (" << this->direction.getX() << ", " << this->direction.getY() << ")" << endl;
		cout << "Speed: " << this->speed << endl;
		cout << "Mass: " << this->mass << endl;

		cout << "\nWidth: " << this->width << endl;
		cout << "Height: " << this->height << endl;

		cout << "\n======================================================\n" << endl << endl;
	}
#pragma endregion


#pragma region ICOLLISION_2D_METHODS
	//M005: THIS -> Box2D collision check
	bool Box2D::collision(const Box2D& box)
	{
		float distX, distY;
		bool hasCollided_X = false, hasCollided_Y = false;

		// this.PosX >= rhs.PosX
		if ( this->position.getX() >= box.position.getX() )
		{
			distX = this->position.getX() - box.position.getX();
			hasCollided_X = ( distX <= box.getWidth() ) ? true : false;
		}
		else
		{
			distX = box.position.getX() - this->position.getX();
			hasCollided_X = ( distX <= this->width ) ? true : false;
		}

		// this.PosY >= rhs.PosY
		if ( this->position.getY() >= box.position.getY() )
		{
			distY = this->position.getY() - box.position.getY();
			hasCollided_Y = ( distY <= box.getHeight() ) ? true : false;
		}
		else
		{
			distX = box.position.getY() - this->position.getY();
			hasCollided_Y = ( distX <= this->height ) ? true : false;
		}

		return ( hasCollided_X !=false && hasCollided_Y !=false ) ? true : false;;
	}

	//M006: THIS -> Circle2D collision check
	bool Box2D::collision(const Circle2D& circle)
	{
		Vector2 center = circle.getCenter();

		float sqrDist = MathHelper::sqrDistancePointToBox(center, *this);
		float sqrRadius = circle.getRadius() * circle.getRadius();

		return ( (sqrDist > sqrRadius) ?  false :  true );
	}
#pragma endregion


#pragma region SHAPE_2D_METHODS
	//M007: Getter Center
	const Vector2 Box2D::getCenter() const
	{
		float x = this->position.getX() + this->width/2;
		float y = this->position.getY() - this->height/2;

		return Vector2(x, y);
	}


	//==================== SETTER: ROTATION_2D ====================
	//
	//M008: Rotate 2D shape using degrees
	void Box2D::rotateInDegs(const float degrees)
	{
		Vector2 vec = MathHelper::degreesToVector2(degrees);
		vec = (vec + this->direction);
		vec.normalize();
		this->direction = vec;
	}

	//M009: Rotate 2D shape using radians
	void Box2D::rotateInRads(const float radians)
	{
		Vector2 vec = MathHelper::radiansToVector2(radians);
		vec = (vec + this->direction);
		vec.normalize();
		this->direction = vec;
	}
	//==================== SETTER: ROTATION_2D ====================


	//==================== SETTER: TRANSLATION_2D ====================
	//
	//-------------------------------------------------
	//		TRANSLATION_POSITION
	//-------------------------------------------------
	//
	//M010: Translate position using floats
	void Box2D::translatePosition(const float X, const float Y)
	{
		this->position.setXY( this->position.getX() + X, this->position.getY() + Y );
	}

	//M011: Translate position using Vector2
	void Box2D::translatePosition(const Vector2 &value)
	{
		this->position.setXY( this->position + value );
	}


	//-------------------------------------------------
	//		TRANSLATION_DIRECTION
	//-------------------------------------------------
	//
	//M012: Translate direction using floats
	void Box2D::translateDirection(const float X, const float Y)					
	{
		Vector2 vec(X, Y);
		vec = (vec + this->direction);
		vec.normalize();
		this->direction = vec;
	}

	//M013: Translate direction using Vector2
	void Box2D::translateDirection(const Vector2 &value)						
	{
		Vector2 vec = value;
		vec = (vec + this->direction);
		vec.normalize();
		this->direction = vec;
	}
	//==================== SETTER: TRANSLATION_2D ====================
#pragma endregion

} /* NAMESPACE */
